package com.fast.fast.common.gateway.config;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.router.SaHttpMethod;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.fast.fast.common.base.constant.CacheConstants;
import com.fast.fast.common.base.constant.enums.HttpStatusEnum;
import com.fast.fast.common.base.vo.Result;
import com.fast.fast.common.redis.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

/**
 * Sa-Token配置类
 *
 * @author lyf
 * @date 2022/01/01 00:00 周六
 **/
@Slf4j
@Configuration(proxyBeanMethods = false)
public class SaTokenConfig {

    @Autowired
    private RedisUtils redisUtils;

    /**
     * 配置Sa-Token
     * 此配置会覆盖 application.yml 中的配置
     *
     * @return
     */
    @ConditionalOnProperty(prefix = "sa-token", name = "open", havingValue = "true")
    @Bean
    @Primary
    public cn.dev33.satoken.config.SaTokenConfig getSaTokenConfigPrimary() {
        return new cn.dev33.satoken.config.SaTokenConfig()
                // token名称(同时也是cookie名称)。默认值：satoken
                .setTokenName("satoken")
                // token有效期，单位：秒。默认值：2592000秒（30天）
                .setTimeout(CacheConstants.GLOBAL_REDIS_TTL.getSeconds())
                // token临时有效期(指定时间内无操作就视为token过期)，单位：秒，-1表示永不过期。默认值：-1
                .setActivityTimeout(-1)
                // 是否允许同一账号并发登录(为true时允许并发登录，为false时新登录挤掉旧登录)。默认值：true
                .setIsConcurrent(true)
                // 在多人登录同一账号时，是否共用一个token(为true时所有登录共用一个token，为false时每次登录新建一个token)。默认值：true
                .setIsShare(true)
                // token风格。默认值：uuid
                .setTokenStyle("uuid")
                // 是否输出操作日志。默认值：false
                .setIsLog(false);
    }

    /**
     * Sa-Token全局过滤器
     *
     * @return
     */
    @ConditionalOnProperty(prefix = "sa-token", name = "open", havingValue = "true")
    @Bean
    public SaReactorFilter getSaReactorFilter() {
        return new SaReactorFilter()
                // 拦截的url
                .addInclude("/**")
                // 开放的url
                .addExclude("/favicon.ico")
                // 前置函数：在每次认证函数之前执行
                .setBeforeAuth(obj -> {
                    // ---------- 设置跨域响应头 ----------
                    SaHolder.getResponse()
                            // 允许指定域访问跨域资源
                            .setHeader("Access-Control-Allow-Origin", "*")
                            // 允许的header参数
                            .setHeader("Access-Control-Allow-Headers", "*")
                            // 允许所有请求方式
                            .setHeader("Access-Control-Allow-Methods", "*")
                            // 有效时间
                            .setHeader("Access-Control-Max-Age", "3600");
                    // 如果是预检请求，则立即返回到前端
                    SaRouter.match(SaHttpMethod.OPTIONS)
                            .free(r -> {
                                //System.out.println("--------OPTIONS预检请求，不做处理--------");
                            })
                            .back();
                })
                // 鉴权方法
                .setAuth(obj -> {
                    SaRouter.match("/**")
                            .notMatch("/getCaptcha", "/login", "/logout", "/**/File/**")
                            .check(r -> StpUtil.checkLogin());
                })
                // 异常处理方法：每次setAuth函数出现异常时进入
                .setError(e -> {
                            // 设置响应头
                            SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8");
                            // 设置响应状态码
                            SaHolder.getResponse().setStatus(HttpStatusEnum.FORBIDDEN.getCode());
                            return JSON.toJSON(Result.error(e.getMessage(), HttpStatusEnum.FORBIDDEN.getCode()));
                        }
                );
    }

}
